/**
 * 创建websocket实例 可断线重连
 * @method createSocket
 * @param {String} url socket路径
 * @param {Object} handlers socket各路回调 对象包含onopen, onmessage, onclose, onerror函数
 * @return {WebSocket} websocket实例
 */
const createSocket = function (url, handlers) {
  let retries = 0
  let sock = null
  const { onopen, onmessage, onclose, onerror } = handlers

  function socket (url) {
    sock = new WebSocket(url)

    sock.onopen = function (e) {
      // console.log(e, 'onopen')
      heartCheck.reset().start()
      retries = 0
      typeof onopen === 'function' && onopen(sock)
    }

    sock.onerror = function (e) {
      // console.log(e, 'onerror')
      typeof onerror === 'function' && onerror(e)
    }

    sock.onclose = function (e) {
      // console.log(e, 'onclose')
      typeof onclose === 'function' && onclose(e)
      heartCheck.reset()
      sock = null
      reconnect(e)
    }

    sock.onmessage = function (e) {
      // console.log(e, 'onmessage')
      heartCheck.reset().start()
      typeof onmessage === 'function' && onmessage(e)
    }

    /**
     * 断开重连，重连10次后仍连不上则不再重新连接
     * 每次重连的时间间隔递增
     */
    const reconnect = (e) => {
      if (!e.wasClean && retries <= 10) {
        const retryInMs = 1000 * Math.pow(2, retries) + Math.random() * 100
        retries += 1
        setTimeout(function () {
          console.log('正在尝试重新连接....')
          socket(url)
        }, retryInMs)
      }
    }

    /**
     * 心跳检测机制
     */
    const heartCheck = {
      timeout: 10 * 1000,
      timeoutObj: null,
      serverTimeoutObj: null,
      reset () {
        clearTimeout(this.timeoutObj)
        clearTimeout(this.serverTimeoutObj)
        return this
      },
      start () {
        const self = this
        this.timeoutObj = setTimeout(function () {
          sock && sock.readyState === 1 && sock.send('ping')
          self.serverTimeoutObj = setTimeout(function () {
            sock && sock.close()
          }, self.timeout)
        }, this.timeout)
      }
    }
  }
  socket(url)
  return sock
}
export default createSocket
